Disk Encryption in NVIDIA Jetson platforms

From RidgeRun Developer Wiki


NVIDIA partner logo NXP partner logo






This wiki shows how to implement Disk Encryption for NVIDIA Jetson Platforms and will also show how to flash the encrypted partition. This tutorial was tested with a Jetson Orin Nano module running JetPack 6; keep in mind that for other versions of JetPack, some steps may vary slightly. It is assumed that JetPack sources were already installed and contained in a directory used to initially flash the target board.

Implementation

In order to enable Disk Encryption for a Jetson module, a host machine is required where JetPack sources were previously installed and that contains a directory initially used to flash the board.

Dependencies

As a first step for the implementation, install the following packages:

sudo apt-get install python3-cryptography python3-cffi-backend libxml2-utils

sudo apt-get install cryptsetup python3-pycryptodome python3-crypto

Partition layout

When the packages are ready, the next step is to define the partition layout. By default, the original build uses the partition layout defined in Linux_for_Tegra/‌bootloader/‌generic/cfg/flash_t234_qspi_sdmmc.xml, which can be used as a template to define the desired partition layout. Here is a snippet showing how a partition is defined:

<partition name="APP" type="data">
    <allocation_policy> sequential </allocation_policy>
    <filesystem_type> basic </filesystem_type>
    <size> APPSIZE </size>
    <file_system_attribute> 0 </file_system_attribute>
    <allocation_attribute> 0x808 </allocation_attribute>
    <align_boundary> 16384 </align_boundary>
    <percent_reserved> 0 </percent_reserved>
    <unique_guid> APPUUID </unique_guid>
    <filename> APPFILE </filename>
    <description> **Required.** Contains the rootfs. This partition must be assigned
        the "1" for id as it is physically put to the end of the device, so that it
        can be accessed as the fixed known special device `/dev/mmcblk0p1`. </description>
</partition>

In order to create an encrypted partition, it is necessary to set its encrypted attribute, which is done by adding the option encrypted="true". JetPack sources include an example of a partition layout which includes an encrypted partition called APP_ENC, this example is located in Linux_for_Tegra/‌bootloader/generic/cfg/flash_t234_qspi_sdmmc_enc_rfs.xml. This tutorial uses the example partition layout with the encrypted APP_ENC partition. Here is a snippet of the file where the encrypted partition is defined:

<partition name="APP" id="1" type="data">
    <allocation_policy> sequential </allocation_policy>
    <filesystem_type> basic </filesystem_type>
    <size> 419430400 </size>
    <file_system_attribute> 0 </file_system_attribute>
    <allocation_attribute> 0x8 </allocation_attribute>
    <percent_reserved> 0 </percent_reserved>
    <align_boundary> 16384 </align_boundary>
    <unique_guid> APPUUID </unique_guid>
    <filename> system_boot.img </filename>
    <description> **Required.** Contains the boot partition. This partition must be defined
        after `primary_GPT` so that it can be accessed as the fixed known special device
        `/dev/mmcblk0p1`. </description>
    </partition>

    <partition name="APP_ENC" id="2" type="data" encrypted="true" reencrypt="false">
    <allocation_policy> sequential </allocation_policy>
    <filesystem_type> basic </filesystem_type>
    <size> APP_ENC_SIZE </size>
    <file_system_attribute> 0 </file_system_attribute>
    <allocation_attribute> 0x8 </allocation_attribute>
    <percent_reserved> 0 </percent_reserved>
    <align_boundary> 16384 </align_boundary>
    <unique_guid> APP_ENC_UUID </unique_guid>
    <filename> system_root_encrypted.img </filename>
    <description> **Required.** Contains the encrypted root partition("/"). </description>
</partition>

Generating the EKS image

After this, the next step is to generate the disk encryption key and the EKS image to be flashed on the board. To do this, the gen_ekb.py utility is required. This utility is part of the OP-TEE source package, so it is necessary to obtain it, to do so check our OP-TEE Guide for NVIDIA Jetson, also keep in mind that OP-TEE is required for Disk Encryption to work in Jetson platforms. After obtaining the sources go to the following path:

cd optee/samples/hwkey-agent/host/tool/gen_ekb/

The gen_ekb.py utility and an example.sh script are located here. This script will show you how to use the utility or generate the EKS image. Regardless of your preferred option, it is necessary to generate the required keys. It is important to remember that if the OEM_K1 fuse was previously burned in the board, the same key must be used to generate the EKS image or the board will not boot. If the fuse was not burned previously, the key must be generated. It is not necessary to burn the fuse in order to enable disk encryption, but it might be prefered to do so as an extra layer of security, check our Secure Boot section to learn more about burning fuses. For this guide, the OEM K1 fuse is not burned.

Generating keys

To generate the required keys, run the following commands:

openssl rand -rand /dev/urandom -hex 32 > oem_k1.key #OEM K1 key

openssl rand -rand /dev/urandom -hex 32 > sym_t234.key    # kernel/kernel-dtb encryption key

openssl rand -rand /dev/urandom -hex 16 > sym2_t234.key   # disk encryption key

openssl rand -rand /dev/urandom -hex 16 > auth_t234.key   # uefi variables authentication key

Remember to keep these keys stored securely. After generating the keys, use the example.sh script to generate the EKS image:

./example.sh

After executing the script the eks_t234.img file for Orin platforms is created. In order to use the new image the next time the board is flashed, copy the file to the Linux_for_Tegra/bootloader path. After this, copy the disk encryption key to the Linux_for_Tegra/ directory. With the tutorial's steps, the disk encryption key is in the sym2_t234.key file.

Flashing the board

The next step is to flash the board. For this, the board needs to be in recovery mode. In the case of the Jetson Orin Nano Devkit, set it to recovery mode by shorting pins 9 and 10 in the J14 header, the pins are labeled as FC_REC and GND respectively. After shorting the pins, plug the power adapter into the board and connect the board to the host machine using the USB-C port. Proceed to flash the board:

cd Linux_for_Tegra/

sudo ROOTFS_ENC=1 ./flash.sh -i "./disk_enc.key" <board> <rootdev>

# Example for the Jetson Orin Nano using an SD card:
sudo ROOTFS_ENC=1 ./flash.sh -i "./sym2_t234.key" jetson-orin-nano-devkit mmcblk0p1

The ROOTFS_ENC=1 option will enable disk encryption for the created image in the selected storage device. After completing the flashing process, the board will be have disk encryption enabled.

Testing the integration

After flashing the board, verify that disk encryption was enabled correcly by using the lsblk command. If the example flash_t234_qspi_sdmmc_enc_rfs.xml partition layout was used, the output will look like the following:

lsblk 

# Output
NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0    16M  1 loop  
mmcblk0        179:0    0  59.4G  0 disk  
├─mmcblk0p1    179:1    0   400M  0 part  /boot
├─mmcblk0p2    179:2    0  54.6G  0 part  
│ └─crypt_root 253:0    0  54.6G  0 crypt /
├─mmcblk0p3    179:3    0   128M  0 part  
├─mmcblk0p4    179:4    0   768K  0 part  
├─mmcblk0p5    179:5    0  31.6M  0 part  
├─mmcblk0p6    179:6    0   128M  0 part  
├─mmcblk0p7    179:7    0   768K  0 part  
├─mmcblk0p8    179:8    0  31.6M  0 part  
├─mmcblk0p9    179:9    0    80M  0 part  
├─mmcblk0p10   179:10   0   512K  0 part  
├─mmcblk0p11   179:11   0    64M  0 part  /boot/efi
├─mmcblk0p12   179:12   0    80M  0 part  
├─mmcblk0p13   179:13   0   512K  0 part  
├─mmcblk0p14   179:14   0    64M  0 part  
├─mmcblk0p15   179:15   0   400M  0 part  
│ └─crypt_UDA  253:1    0   384M  0 crypt /mnt/crypt_UDA
└─mmcblk0p16   179:16   0 479.5M  0 part  
zram0          252:0    0   635M  0 disk  [SWAP]
zram1          252:1    0   635M  0 disk  [SWAP]
zram2          252:2    0   635M  0 disk  [SWAP]
zram3          252:3    0   635M  0 disk  [SWAP]
zram4          252:4    0   635M  0 disk  [SWAP]
zram5          252:5    0   635M  0 disk  [SWAP]

It is also possible to verify that the encryption works as expected by flashing the board with a different encryption key than the one used to create the eks_t234.img. If the key used at the moment of flashing the board with the ./flash.sh script is different to the one used when creating the eks_t234.img with the ./example.sh script or the gen_ekb.py tool, the board will be flashed successfully but it will get stuck at boot time. With access to a monitor or a serial connector, it is possible to see that the board is unable to unlock the storage device and a message similar to the following is shown:

No key available with this passphrase

ERROR: failed to unlock the encrypted dev /dev/mmcblk0p2

Then, if after flashing the board using the correct key, it will boot successfully.

Creating Encrypted Partitions Dynamically

After enabling disk encryption in the board it is also possible to create encrypted partitions at run time. Keep in mind that this only applies if disk encryption was previously enabled following the steps in the previous section. Also, it is important to know that the encrypted partition will be created based on an already existing partition; this partition will be formatted, so any data stored in that partition will be lost if not backed up.

First, select which partition is going to used to create the new encrypted partition. Check information on the existing partitions by running the following command:

sudo blkid

# Example output
/dev/mmcblk0p12: PARTLABEL="recovery_alt" PARTUUID="1d0b754f-7542-46d2-b84e-95439c28a221"

After selecting the partition to be used, create the encrypted partition with the gen_luks.sh tool:

sudo gen_luks.sh <partition> <encrypted partition name>

# Example
sudo gen_luks.sh /dev/mmcblk0p12 crypt_DATA

When the previous command is executed, the tool will be started, getting the following message: </syntaxhighlight>

After selecting the partition to be used, create the encrypted partition with the gen_luks.sh tool:

All data on /dev/mmcblk0p12 will be wiped out after luks disk is created. Reply YES to continue:

Enter "YES" to continue, or "No" to cancel the process. Then, the tool will ask about the file system type to use:

Do you want to format the encrypted partition crypt_DATA into ext4? Reply YES or No:

By default the ext4 type is used, so if the input is "YES" the partition will be created with this format. If "No" is selected, the file system type can be set after rebooting the device. To finish the process, it is necessary to reboot the device, so the tool will ask if you want to reboot now:

Do you want to reboot the device to create encrypted partition? Reply YES or No:

If you select "YES" the device will reboot, if you select "No" the device will not reboot at the moment but the encrypted partition will be created only after the board is rebooted. After rebooting, the encrypted partition is created at /dev/mapper/crypt_DATA and mounted at /mnt/crypt_DATA. You check this with the lsblk command:

lsblk

# Output
NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINTS
loop0            7:0    0    16M  1 loop  
mmcblk0        179:0    0  59,4G  0 disk  
├─mmcblk0p1    179:1    0   400M  0 part  /boot
├─mmcblk0p2    179:2    0  54,6G  0 part  
│ └─crypt_root 253:0    0  54,6G  0 crypt /
├─mmcblk0p3    179:3    0   128M  0 part  
├─mmcblk0p4    179:4    0   768K  0 part  
├─mmcblk0p5    179:5    0  31,6M  0 part  
├─mmcblk0p6    179:6    0   128M  0 part  
├─mmcblk0p7    179:7    0   768K  0 part  
├─mmcblk0p8    179:8    0  31,6M  0 part  
├─mmcblk0p9    179:9    0    80M  0 part  
├─mmcblk0p10   179:10   0   512K  0 part  
├─mmcblk0p11   179:11   0    64M  0 part  /boot/efi
├─mmcblk0p12   179:12   0    80M  0 part  
│ └─crypt_DATA 253:2    0    64M  0 crypt /mnt/crypt_DATA
├─mmcblk0p13   179:13   0   512K  0 part  
├─mmcblk0p14   179:14   0    64M  0 part  
├─mmcblk0p15   179:15   0   400M  0 part  
│ └─crypt_UDA  253:1    0   384M  0 crypt /mnt/crypt_UDA
└─mmcblk0p16   179:16   0 479,5M  0 part  
zram0          252:0    0   635M  0 disk  [SWAP]
zram1          252:1    0   635M  0 disk  [SWAP]
zram2          252:2    0   635M  0 disk  [SWAP]
zram3          252:3    0   635M  0 disk  [SWAP]
zram4          252:4    0   635M  0 disk  [SWAP]
zram5          252:5    0   635M  0 disk  [SWAP]